


// vue3 响应式原理
let toProxy = new WeakMap() // 弱引用映射表  存放原对象：代理过的对象
let toRaw = new WeakMap()   // 代理过的对象：原对象

// 判断对象
function isObject(val) {
    return typeof val === 'object' && val !== null
}

function hasOwn(target, key) {
    return target.hasOwnProperty(key)
}

// 核心方法
function reactive(target) {
    return createReactiveObject(target)
}

// 创建响应式对象
function createReactiveObject(target) {
    if (!isObject(target)) {    // 原始类型，直接返回
        return target
    }

    let proxy = toProxy.get(target)
    if (proxy) {    // 代理过
        return proxy
    }
    if (toRaw.has(target)) {    // 防止多层代理
        return target
    }

    let baseHandler = {
        get(target, key, receiver) {
            // reflect对象上的方法与proxy上的方法一一对应
            let res = Reflect.get(target, key, receiver) // reflect 做反射
            return isObject(res) ? reactive(res) : res
        },
        set(target, key, value, receiver) { // [1,2,3,4]
            // 判断是新增属性还是修改属性
            let hadKey = hasOwn(target, key)
            let oldValue = target[key]
            let res = Reflect.set(target, key, value, receiver)

            if (!hadKey) {
                console.log('新增属性');
            } else if (oldValue !== value) {
                console.log('修改属性');
            }
            return res
        },
        deleteProperty(target, key) {
            let res = Reflect.deleteProperty(target, key)
            console.log('删除属性');
            return res
        }
    }

    let observed = new Proxy(target, baseHandler)
    toProxy.set(target, observed)
    toRaw.set(observed, target)
    return observed
}

// 数组
let users = ['cat', 'dog', 'mice']
let myProxy = reactive(users)
// （1）当给数组添加元素时，'新增属性'
myProxy.push('fish')

// （2）当改变数组原本长度时，'修改属性'
myProxy.length = 5

// （3）当进行多层代理时， 利用get读取时才代理
let otherProxy = reactive({
    name: 'bird',
    food: {
        type: 'meat'
    },
    hobbies: ['flying', 'singing']
})
// 当修改基本类型的属性值，'修改属性'
otherProxy.name = 'duck'
// 当删除基本类型的属性时，'删除属性'
delete otherProxy.name
console.log(otherProxy.name); //undefined

// 当改变对象类型的属性时,'修改属性'
otherProxy.food.type = 'leaves'
console.log(otherProxy.food.type); //leaves

// 被代理过的对象需要记录一下，不要再次代理了
// reactive(otherProxy)